Arm Performance Libraries替代Eigen的后端
在将 Eigen 的后端替换为 Arm Performance Libraries(APL,Arm 性能库,包含 Arm BLAS、LAPACK 等线性代数优化实现)时,核心思路是让 Eigen 调用 APL 提供的经过 Arm 架构优化的底层线性代数函数(如矩阵乘法、分解等),而非 Eigen 自带的通用实现,从而利用 APL 针对 Arm CPU(如 A55 等)的指令集(如 NEON)和架构特性优化,提升计算性能。以下是具体步骤和注意事项:
一、原理:Eigen 对外部 BLAS/LAPACK 的支持
Eigen 本身是一个纯头文件库,其底层线性代数运算默认使用自身的手写实现(部分优化了 NEON 指令),但也支持通过 接口适配 调用外部优化的 BLAS(基础线性代数子程 序)和 LAPACK(线性代数包)库(如 APL、OpenBLAS、MKL 等)。
当 Eigen 检测到外部 BLAS/LAPACK 时,会优先调用这些库的高效实现(尤其是大矩阵运算,如 gemm
矩阵乘法、gesvd
奇异值分解等),从而提升性能。
二、具体步骤:用 APL 替代 Eigen 后端
1. 安装 Arm Performance Libraries
首先需要在目标 Arm 平台(如 A55 架构的边缘设备)上安装 APL。
- 下载:从 Arm 官网获取适用于 Arm 64 位(aarch64)的 APL 安装包(需注册,Arm 开发者官网)。
- 安装:按官方指南安装,默认路径通常为
/opt/arm/armpl/<版本号>/
,包含头文件(include/
)和库文件(lib/
,如libarmpl_lp64.so
等,lp64
对应 64 位模式)。
2. 配置 Eigen 以启用外部 BLAS/LAPACK 支持
Eigen 通过宏定义控制是否启用外部 BLAS/LAPACK,需在编译时指定相关宏和链接选项。
(1)修改 Eigen 配置(或编译宏)
在代码中 包含 Eigen 头文件前 定义以下宏,或在编译命令中通过 -D
传入:
cpp
// 启用 Eigen 对外部 BLAS 的支持
#define EIGEN_USE_BLAS
// 启用 Eigen 对外部 LAPACK 的支持(如需矩阵分解等高级功能)
#define EIGEN_USE_LAPACK
这些宏会让 Eigen 在编译时优先调用外部 BLAS(如 APL 的 cblas_*
函数)和 LAPACK(如 lapack_*
函数)。
(2)编译时链接 APL 库
编译代码时,需要指定 APL 的头文件路径、库路径,并链接 APL 的 BLAS/LAPACK 库。
以 g++
为例,编译命令需包含:
bash
# 头文件路径(APL 的 include 目录)
-I/opt/arm/armpl/<版本号>/include
# 库文件路径(APL 的 lib 目录)
-L/opt/arm/armpl/<版本号>/lib
# 链接 APL 的 BLAS/LAPACK 库(根据 APL 版本可能需要调整库名)
-larmpl_lp64 -larmpl_lp64_mp # 64位模式的库,具体名称以安装目录为准
# 若 APL 依赖其他库(如 pthread),需补充链接
-lpthread -lm
3. 验证是否成功调用 APL
替换后需验证 Eigen 是否确实使用了 APL 的后端,避免仍使用 Eigen 自带实现:
-
方法 1:编译日志检查
编译时若出现类似Eigen::blas::gemm
调用的提示(或无报错),说明宏定义生效。 -
方法 2:性能对比
运行包含大矩阵运算(如matrixA * matrixB
、matrix.jacobiSvd()
)的代码,对比替换前后的耗时。若 APL 适配正确,大矩阵运算性能应显著提升(小矩阵可能因调用开销差异不明显)。 -
方法 3:工具调试
使用ldd
命令检查生成的可执行文件是否链接了 APL 的库(如libarmpl_lp64.so
):bash
ldd your_executable | grep armpl
三、注意事项
-
适用场景
APL 对 大矩阵运算(如维度 > 100 的矩阵乘法、SVD 分解)优化明显,而对小矩阵(如 VIO 中的 3x3 矩阵)可能不如 Eigen 自带的 NEON 优化高效(因函数调用开销占比高)。需结合实际算法(如 VINS-Fusion 中的滑窗优化、Fast-LIO 中的状态估计)的矩阵规模判断收益。 -
数据类型兼容性
Eigen 默认使用列主序(Column-major)存储,与 BLAS 标准一致,无需调整数据布局;但需确保 Eigen 的数据类型(如float
/double
)与 APL 编译时的精度(lp64
对应 64 位整数 + 32 位 float/64 位 double)匹配。 -
部分函数不支持
Eigen 中的部分高级功能(如稀疏矩阵、自定义算子)可能不调用外部 BLAS/LAPACK,仍需依赖 Eigen 自身实现,需针对性优化(如手动调用 APL 接口)。 -
与其他优化结合
替换后端后,可配合其他优化(如编译器-O3 -march=armv8.2-a+neon
选项、OpenMP 多线程并行)进一步提升性能(APL 本身也支持多线程,可通过环境变量ARMPL_NUM_THREADS
控制线程数)。 -
版本兼容性
确保 Eigen 版本(建议 3.3 以上)支持外部 BLAS/LAPACK,且 APL 版本与目标 Arm 架构(如 A55 属于 Armv8.2-A)匹配,避免指令集不兼容(如 APL 编译时启用了 A55 不支持的指令)。
四、替代方案:直接调用 APL 接口
若 Eigen 对 APL 的适配不够理想,可在关键计算模块(如 VIO 中的协方差矩阵更新、LIO 中的点云配准)直接调用 APL 的 C 接口(如 cblas_sgemm
进行单精度矩阵乘法),跳过 Eigen 中间层,示例:
cpp
#include "armpl.h" // APL 的头文件
// 计算 C = alpha*A*B + beta*C(A: MxK, B: KxN, C: MxN,列主序)
cblas_sgemm(CblasColMajor, CblasNoTrans, CblasNoTrans,
M, N, K, alpha, A, lda, B, ldb, beta, C, ldc);
通过以上方法,可让 Arm 架构的边缘设备(如 8 核 A55)在路径规划、VIO、LIO 等算法的线性代数密集型任务中,充分利用 APL 的硬件优化,提升运行效率。